因為昨天文章發的有點匆忙,
今天的文章會再補充一下昨天介紹的 XHR,
然後練習 fetch 的用法~
昨天練習了用 XHR 拿資料的方法,
但是有沒有覺得它用起來有點麻煩?
前面都要先宣告 XMLHttpRequest 的物件,
然後還得寫 open 跟 send 才會真的發送請求出去:
const xhr = new XMLHttpRequest();
  xhr.open("get", sourceUrl, true);
  xhr.send(null);
再來好不容易發送請求出去了,
你還沒辦法直接取用 call API 後回傳的資料,
還得再多寫 xhr.onload,
這時候才拿得到資料才能做後續的處理:
xhr.onload = function () {
		setdataResult(JSON.parse(xhr.responseText));
	};
}
而且如果是直接在 xhr.onload 裡面寫變數,
因為是在 function 內,
function 外沒辦法直接取得這個變數(作用域的關係),
例如這邊將昨天的例子改寫成這樣:
function fetchData(){
    const xhr = new XMLHttpRequest();
    xhr.open("get", sourceUrl, true);
    xhr.send(null);
    xhr.onload = function () {
      result = JSON.parse(xhr.responseText);
      console.log(result);
    };
    console.log(result);
  }
然後文字的地方改成直接取用 {result}
<Text my={4} fontSize="p" textAlign="center">
  result: {result?.records?.location?.length}
</Text>

這樣在 xhr.onload 外面其實是拿不到資料的(要另外處理),
所以昨天後來我是用 useState 去改值(將拿到的資料設定進去),
這樣是不是有夠麻煩orz
因為 XMLHttpRequest 算是很陽春的 AJAX 實作方法,
所以之後也出現了幾個好用的工具,
我覺得應該是為了克服 XHR 的一些缺點啦。
像是今天要練習的 fetch 就是其中一個,
而 fetch 是使用 Promise 物件的寫法,
那 Promise 又是什麼呢?
Promise 要認真說的話又可以獨立一篇了XD
這邊推薦可以看大大的文章 → JavaScript Promise 全介紹
我自己簡單的理解就是,Promise 是為了優化 Javascript 的 非同步 語法部份,
例如在寫 Javascript 時你會發現你寫在前後行的語法(EX: 執行A; 執行B;),
但並不會真的照你寫的順序執行,
而是"同時"執行,
所以如果你需要寫 A執行完才要執行B 的語法,Promise 就能解決這個問題~
前情提要講完了,
那我們接下來就看一下 fetch 的簡單寫法吧:
function fetchData(){
	fetch(sourceUrl)
	.then(function(response) {
	  return response.json();
	})
	.then(function(myJson) {
	  console.log(myJson);
	});
}
簡單說明一下,fetch(sourceUrl) 這邊意思就是傳送要求到 sourceUrl,
等到拿到 response 之後(.then) 回傳 response.json(),
接著(.then) 再用 console.log 把 response.json() 印出來,
像這樣:
不需要特別寫 open, send, onload 就能完成 call API 並拿到回傳資料,
這樣是不是簡單多了呢?
既然 fetch 拿資料這麼方便,
那我們今天用拿到的資料來簡單寫一下應用好了。
首先在動手前,昨天也有提到我會習慣先觀察一下資料:
https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=rdec-key-123-45678-011121314

題外話,你可能會發現資料長得很密密麻麻要怎麼看?
這邊推薦可以裝 Chrome 的擴充套件 JSON Viewer,
然後它就會把 JSON 轉換成比較可讀的樣子,
像這樣:
從資料裡面可以看到有各縣市的明日天氣預報資料,
現在假設我明天要去臺中玩(並沒有T_T),
我想關心一下臺中明天的天氣情況,
這邊就稍微處理一下拿到的資料,
我習慣是觀察好資料結構後,先拿一筆資料試寫看看(這邊容我這邊快轉一下):
function App() {
  const sourceUrl =
    "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=rdec-key-123-45678-011121314";
  const [dataResult, setdataResult] = useState([]);
  const searchArea = '臺中市';
  const [dataSearchArea, setDataSearchArea] = useState({});
  let result = [];
  function fetchData(){
    fetch(sourceUrl)
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      setdataResult(myJson.records.location);
    });
  }
  useEffect(() => {
    setDataSearchArea(dataResult.filter((d) => d.locationName === searchArea));
  }, [dataResult]);
  return (
    <ChakraProvider theme={theme}>
      <>
        <Text my={4} fontSize="p" textAlign="center">
          sourceUrl: {sourceUrl}
        </Text>
        <Text my={4} as="h2" fontSize="2xl" textAlign="center">
          全部總共 {dataResult.length === 0 ? 0 : dataResult?.length } 筆資料
        </Text>
        <Divider my={4} />
        <Text my={4} as="h3" fontSize="xl" textAlign="center">
          區域: {searchArea}
        </Text>
        <Text px={4}>
          [明日天氣預報]
          <br />
          時間:{dataSearchArea[0]?.weatherElement[0]?.time[0]?.startTime}
          {' ~ '}
          {dataSearchArea[0]?.weatherElement[0]?.time[0]?.endTime}
          <br />
          天氣:{dataSearchArea[0]?.weatherElement[0]?.time[0]?.parameter?.parameterName}
        </Text>
        <Divider my={4} />
        <Center>
          <Button onClick={fetchData}>取得資料</Button>
        </Center>
      </>
    </ChakraProvider>
  );
}

然後從資料我們可以看到每天的預報資料有 3 筆 (在 time 裡面),
所以先用一筆確定是你要的畫面之後,
我就會把 time 用 map 以迴圈的方式讀取,把 3 筆資料都渲染出來,
像這樣:
taSearchArea[0]?.weatherElement[0]?.time?.map((t) => (
    <Box>
      時間:{t.startTime}
      {' ~ '}
      {t.endTime}
      <br />
      天氣:{t.parameter?.parameterName}
    </Box>
  ))}

這樣你就可以看到臺中市的明天天氣預報資料都出現了~(看起來不會下雨真是太好了但我不會去臺中玩QQ)
到這邊千萬不要高興太早,
如果你去 fetch 遇到 error (EX. 目的地server掛掉,或網址打錯...等)
那會怎樣呢?
這邊我故意把 url 多打字來試試看:
你可以看到畫面上都沒有資料,
打開 console 來看可以看到有 error
(PS. 其實一般來說,應該是瀏覽器會直接跳 error 畫面,
所以 error 是需要另外處理的,
但可能我裝的套件有特別處理這個事件,所以畫面上很安靜什麼都沒發生)
這種情況該怎麼辦呢?Promise 有提供 catch 的語法,
讓你設定在執行前面動作遇到 error 會執行什麼動作,
像這樣:
fetch(sourceUrl)
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      setdataResult(myJson.records.location);
    })
    .catch((err) => {
      alert("抓取資料錯誤,請確認後再試");
      console.log(err);
    })

這邊你就會看到跳出一個警告視窗顯示:抓取資料錯誤,請確認後再試,
而這個就是 Exception Handling(例外處理),
顧名思義,就是在程式未正常執行時要做什麼處理,
尤其是在跟後端介接 call API 時,例外處理就會變得很重要,
要依據後端給出的 error code 不同時要做不同的處理,
像是前端顯示不同的錯誤訊息,
讓使用者或工程師看到很快知道要怎麼處理這樣~
因此後面的篇章 catch 會常常出現,
也是我們在 call 後端 API 時一定要寫的語法,
今天介紹完 fetch 明天就讓我們進入 axios 的世界吧~~~
request的方式? ajax & fetch & axios
今天終於比較沒那麼趕了orz
感覺好像每年中秋連假都在寫鐵人賽文章中度過XD
真充實XDDDD